package orbito.graphics;

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.geom.AffineTransform;

import orbito.util.GfxUtils;
import orbito.util.Vec2;

public class View {
	
	// The position of this view.
	Vec2 position;
	
	// The orientation of this view.
	double orientation;
	
	// The scale of this view.
	double scale;
	
	// The object that this view is focused on.
	ViewFocus focus;
	
	public View() {
		this(new Vec2(), 0.0, 1.0);
	}
	
	public View(Vec2 position, double orientation, double scale) {
		this.position = position;
		this.orientation = orientation;
		this.scale = scale;
	}
	
	public void renderCameraCross(Graphics2D g, Viewport v, AffineTransform tx, double t, double dt) {
		AffineTransform tr = g.getTransform();
		int h = g.getFontMetrics().getHeight();
		
		// Translate to lower right side of viewport.
		g.translate(v.getWidth() - 130, v.getHeight() - 130);
		
		// Set axis lengths and calculate absolute position.
		Vec2 root = new Vec2(20, 0);
		Vec2 tip = new Vec2(100, 0);
		Vec2 pos = focus != null ? focus.getFocusPosition().add(position) : position;
		
		// Rotate the camera.
		g.rotate(-orientation);
		
		// Render name of focus object, if any.
		String focusName = focus != null ? focus.getFocusName() : "(No Focus)";
		g.setColor(Color.ORANGE);
		g.drawString(focusName, 0, -10);
		
		// Render view zoom.
		g.setColor(Color.ORANGE);
		g.drawString(String.format("%.1fx zoom", Math.log10(scale)), (int) root.x, 15+h+h);
		
		// Convert orientation from radians to degrees in [0, 360].
		double angle = 360 * orientation / (Math.PI * 2);
		while (angle < 0)
			angle += 360;
		while (angle > 360)
			angle -= 360;
		
		// Render view orientation.
		g.setColor(Color.ORANGE);
		g.drawString(String.format("%.1f\u00b0 tilt", angle), (int) root.x, 15+h+h+h);
		
		// Render x-axis and offset.
		g.setColor(Color.BLUE);
		GfxUtils.drawArrow(g, root, tip, 5);
		g.drawString("X", (int) root.x-10, 5);
		g.drawString(String.format("%.5e", pos.x), (int) root.x, 15);
		
		// Rotate for y-axis.
		g.rotate(Math.PI / 2);
		
		// Render y-axis and offset.
		g.setColor(Color.GREEN);
		GfxUtils.drawArrow(g, root, tip, 5);
		g.drawString("Y", (int) root.x-10, 5);
		g.drawString(String.format("%.5e", pos.y), (int) root.x, 15);
		
		g.setTransform(tr);
	}
	
	public AffineTransform getTransform() {
		AffineTransform tr = new AffineTransform();
		tr.scale(scale, scale);
		tr.rotate(-orientation);
		if (focus != null)
			tr.translate(-focus.getFocusPosition().x, -focus.getFocusPosition().y);
		tr.translate(-position.x, -position.y);
		return tr;
	}
	
	public void transformGraphics(Graphics2D g) {
		g.scale(scale, scale);
		g.rotate(-orientation);
		if (focus != null)
			g.translate(-focus.getFocusPosition().x, -focus.getFocusPosition().y);
		g.translate(-position.x, -position.y);
	}
	
	public void translateCamera(double x, double y) {
		position.iadd(new Vec2(x, y).div(scale).rotate(orientation));
	}
	
	public void rotateCamera(double offset) {
		orientation += offset;
	}
	
	public void zoomCamera(double f) {
		// Scale up or down.
		scale += scale * f;
		
		// Assert scale is greater than 0.
		if (scale < Double.MIN_VALUE)
			scale = Double.MIN_VALUE;
	}
	
	public void setPosition(Vec2 pos) {
		if (focus != null) {
			position = focus.getFocusPosition().add(pos);
		} else {
			position = pos;
		}
	}
	
	public Vec2 getPosition() {
		if (focus != null) {
			return focus.getFocusPosition().add(position);
		} else {
			return position;
		}
	}
	
	public void setOrientation(double orientation) {
		this.orientation = orientation;
	}
	
	public double getOrientation() {
		return orientation;
	}
	
	public void setScale(double scale) {
		this.scale = scale;
	}
	
	public double getScale() {
		return scale;
	}
	
	public ViewFocus getFocus() {
		return focus;
	}
	
	public void setFocus(ViewFocus focus) {
		if (focus == null) {
			clearFocus();
		} else {
			this.focus = focus;
			position = new Vec2();
		}
	}
	
	public void resetFocus() {
		if (focus != null) {
			position = new Vec2();
		}
	}
	
	public void clearFocus() {
		if (focus != null) {
			position = focus.getFocusPosition().add(position);
			focus = null;
		}
	}
}
